#include <linux/types.h>
#include "hello_driver.h"
#include <linux/kdev_t.h>
#include <linux/cdev.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/fcntl.h>


static struct cdev MY_CDEV;
static unsigned int MYMAJOR = 0;
static char data[11] = "helloworld";
static struct my_file_private_data MY_PRIVATE_DATA = {
	.data1 = {0},
	.data2 = {0},
};

static int
my_open(struct inode *this_inode, struct file *this_file)
{
	// 这里可以检查 打开的设备文件的 主版本号和次版本号
	// 可以更具 主版本号和次版本号 进行不同的操作
	unsigned int this_minor = iminor(this_inode);
	unsigned int this_major = imajor(this_inode);

	sprintf(MY_PRIVATE_DATA.data1, "major%d\n", this_major);
	sprintf(MY_PRIVATE_DATA.data2, "minor%d\n", this_minor);
	this_file->private_data = &MY_PRIVATE_DATA;

	if ((this_file->f_flags & O_ACCMODE) == O_WRONLY)
	{
		printk(KERN_INFO "major %u mino %u open write\n", this_major, this_minor);
	}
	else
	{
		printk(KERN_INFO "major %u mino %u open\n", this_major, this_minor);
	}
	return 0;
};

static int my_release(struct inode *this_inode, struct file *this_file)
{
	return 0;
};
ssize_t my_read(struct file *this_file, char __user *buf, size_t count, loff_t *f_pos)
{
	ssize_t ret_val;

	if (0 < count && count <= 10)
	{
		unsigned long rest = copy_to_user(buf, data, count);
		if (rest)
		{
			ret_val = -EFAULT;
			goto out;
		}
		else
		{
			return count;
		}
	}
out:
	return ret_val;
};

ssize_t my_write(struct file *this_file, const char __user *buf, size_t count, loff_t *f_pos)
{
	ssize_t ret_val;
	if (count > 0 && count <= 10)
	{
		if (copy_from_user(data, buf, count))
		{
			ret_val = -EFAULT;
			goto out;
		}
		else
		{
			return count;
		}
	}
out:
	return ret_val;
};
static struct file_operations MY_FOP = {
	.owner = THIS_MODULE,
	.read = my_read,
	.write = my_write,
	.open = my_open,
	.release = my_release,
};

static int __init init_function(void)
{
	dev_t my_dev_t;
	int err = alloc_chrdev_region(&my_dev_t, 0, 1, "jihaixuan");
	if (err)
	{
		printk(KERN_WARNING "can not alloc_chrdev_region\n");
		return err;
	}
	MYMAJOR = MAJOR(my_dev_t);

	cdev_init(&MY_CDEV, &MY_FOP);
	MY_CDEV.owner = THIS_MODULE;

	my_dev_t = MKDEV(MYMAJOR, 0);
	err = cdev_add(&MY_CDEV, my_dev_t, 1);
	if (err)
	{
		/*
			这里发生错误 需要 把之前分配的设备号 释放
		*/
		dev_t my_dev_t = MKDEV(MYMAJOR, 0);
		unregister_chrdev_region(my_dev_t, 1);
		printk(KERN_INFO "hello driver go away kernel world\n");
		printk(KERN_NOTICE "init err %d cdev_add(&MY_CDEV,my_dev_t,1)", err);
		return err;
	}
	printk(KERN_INFO "hello driver into kernel world\n");
	return 0;
}
module_init(init_function);

static void __exit exit_function(void)
{
	cdev_del(&MY_CDEV);
	dev_t my_dev_t = MKDEV(MYMAJOR, 0);
	unregister_chrdev_region(my_dev_t, 1);
	printk(KERN_INFO "hello driver go away kernel world\n");
}
module_exit(exit_function);

MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("jihaixuan");
